{
  LX VMath module

  Based on modo's SDK lxvmath.h which is:

  Copyright (c) 2008-2012 Luxology LLC

  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the "Software"),
  to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense,
  and/or sell copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.   Except as contained
  in this notice, the name(s) of the above copyright holders shall not be
  used in advertising or otherwise to promote the sale, use or other dealings
  in this Software without prior written authorization.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  DEALINGS IN THE SOFTWARE.
}

{
  Copyright (c) 2012 David Ballesteros and Jameel Halabi

  This unit is part of the BLAISEmodo wrapper,
  which is licensed under the BSD 2-Clause license (see licenses/license.txt).
}


unit Lx_VMath;

{$mode objfpc}{$H+}
{$asmmode intel}
{$DEFINE SSE2_VMATH}

{$IFDEF SSE2_VMATH}
  {$CODEALIGN LOCALMIN=16}
{$ENDIF}

{.$INLINE OFF}

interface

const
  LXdND        = 2;  { Arrays indexes start anywhere you like in Pascal,
                       but we start at 0 to keep binary compatibility with C }

  LXx_PI       = 3.1415926535897932384626433832795  ;
  LXx_TWOPI    = 6.2831853071795864769252867665590  ;
  LXx_HALFPI   = 1.5707963267948966192313216916398  ;
  LXx_DEG2RAD  = 0.017453292519943295769236907684886;
  LXx_RAD2DEG  = 57.295779513082320876798154814105  ;
  LXx_ROOT2    = 1.4142135623730950488016887242097  ;
  LXx_INVROOT2 = 0.70710678118654752440084436210485 ;
  LXx_ROOT3    = 1.7320508075688772935274463415059  ;

  LXx_BYTE_FRACTION = Single(0.003921568627451);
  LXi_SIMD_MAXRUN   = 512;

type
  PLXtVector   = ^LXtVector  ;
  PLXtFVector  = ^LXtFVector ;
  PLXtSVector  = ^LXtSVector ;
  PLXtUVector  = ^LXtUVector ;
  PLXtFVector2 = ^LXtFVector2;
  PLXtUVector2 = ^LXtUVector2;
  PLXtMatrix   = ^LXtMatrix  ;
  PLXtFMatrix  = ^LXtFMatrix ;
  PLXtMatrix4  = ^LXtMatrix4 ;

  LXtVector    = array [0..LXdND]           of Double  ;
  LXtFVector   = array [0..LXdND]           of Single  ;
  LXtSVector   = array [0..LXdND]           of SmallInt;
  LXtUVector   = array [0..LXdND]           of DWord   ;
  LXtFVector2  = array [0..1    ]           of Single  ;
  LXtUVector2  = array [0..1    ]           of Dword   ;
  LXtMatrix    = array [0..LXdND, 0..LXdND] of Double  ;
  LXtFMatrix   = array [0..LXdND, 0..LXdND] of Single  ;
  LXtMatrix4   = array [0..3    , 0..3    ] of Double  ;

  PLXtBBox = ^LXtBBox;
  LXtBBox  = record
    Min, Max, Extent, Center : LXtVector;
  end;

  generic TGVMath<T> = class
  protected
    type
      V3   = array [0..2] of T;
      V4   = array [0..3] of T;

      TOp1 = function(const A    : T): T of object;
      TOp2 = function(const A, B : T): T of object;

    class function  IfThen(const C: Boolean; RT, RF: T): T; inline;

    class procedure LXx_VUOP   (  var V          : V3    ;
                                const Op         : TOp1  ) ;          inline;
    class procedure LXx_VUOP2  (  var X, V       : V3    ;
                                const Op         : TOp1  ) ;          inline;
    class procedure LXx_VBOP   (  var A          : V3    ;
                                const B          : V3    ;
                                const Op         : TOp2  ) ;          inline;
    class procedure LXx_VBOP3  (  out R          : V3    ;
                                const A, B       : V3    ;
                                const Op         : TOp2  ) ;          inline;
  public
    class procedure LXx_VSET   (  out A          : V3    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_VCLR   (  out A          : V3    ) ;          inline;
    class procedure LXx_VSET3  (  out A          : V3    ;
                                const X, Y, Z    : T     ) ;          inline;
    class function  LXx_VEQS   (const A          : V3    ;
                                const X          : T     ) : Boolean; inline;
    class procedure LXx_VUNIT  (  out A          : V3    ;
                                const K          : UInt32) ;          inline;
    class procedure LXx_VNEG   (  var V          : V3    ) ;          inline;
    class procedure LXx_V1MIN  (  var A          : V3    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_V1MAX  (  var A          : V3    ;
                                const X          : T     ) ;          inline;
    class function  LXx_VNEZERO(const A          : V3    ) : Boolean; inline;
    class procedure LXx_V3SET  (  out A          : V3    ;
                                const X, Y, Z    : T     ) ;          inline;
    class function  LXx_VEQ    (const A, B       : V3    ) : Boolean; inline;
    class procedure LXx_VCPY   (  out A          : V3    ;
                                const B          : V3    ) ;          inline;
    class procedure LXx_VSCL   (  var A          : V3    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_VADD   (  var A          : V3    ;
                                const B          : V3    ) ;          inline;
    class procedure LXx_VSUB   (  var A          : V3    ;
                                const B          : V3    ) ;          inline;
    class procedure LXx_VADDS  (  var A          : V3    ;
                                const B          : V3    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_VMUL   (  var A          : V3    ;
                                const B          : V3    ) ;          inline;
    class procedure LXx_VDIV   (  var A          : V3    ;
                                const B          : V3    ) ;          inline;
    class function  LXxMAX     (const A, B       : T     ) : T      ; inline;
    class function  LXxMIN     (const A, B       : T     ) : T      ; inline;
    class procedure LXx_V3MIN  (  var A          : V3    ;
                                const B          : V3    ) ;          inline;
    class procedure LXx_V3MAX  (  var A          : V3    ;
                                const B          : V3    ) ;          inline;
    class procedure LXx_VADD3  (  out R          : V3    ;
                                const A, B       : V3    ) ;          inline;
    class procedure LXx_VADDS3 (  out R          : V3    ;
                                const A, B       : V3    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_VSUB3  (  out R          : V3    ;
                                const A, B       : V3    ) ;          inline;
    class procedure LXx_VSCL3  (  out R          : V3    ;
                                const A          : V3    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_VMUL3  (  out R          : V3    ;
                                const A, B       : V3    ) ;          inline;
    class procedure LXx_VMIN3  (  out R          : V3    ;
                                const A, B       : V3    ) ;          inline;
    class procedure LXx_VMAX3  (  out R          : V3    ;
                                const A, B       : V3    ) ;          inline;
    class procedure LXx_V4SET4 (  out A          : V4    ;
                                const X, Y, Z, W : T     ) ;          inline;
    class procedure LXx_V4BLACK(  out A          : V4    ) ;          inline;
    class procedure LXx_V4ZERO (  out A          : V4    ) ;          inline;
    class procedure LXx_V4SET  (  out A          : V4    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_V4CPY  (  out A          : V4    ;
                                const B          : V4    ) ;          inline;
    class procedure LXx_V4ADD  (  var A          : V4    ;
                                const B          : V4    ) ;          inline;
    class procedure LXx_V4ADD3 (  out R          : V4    ;
                                const A, B       : V4    ) ;          inline;
    class procedure LXx_V4ADDS (  var A          : V4    ;
                                const B          : V4    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_V4ADDS3(  out R          : V4    ;
                                const A, B       : V4    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_V4SUB  (  var A          : V4    ;
                                const B          : V4    ) ;          inline;
    class procedure LXx_V4SCL  (  var A          : V4    ;
                                const X          : T     ) ;          inline;
    class procedure LXx_V4SCL3 (  out R          : V4    ;
                                const A          : V4    ;
                                const X          : T     ) ;          inline;
    class function  LXx_V4DOT  (const A, B       : V4    ) : T      ; inline;
    class function  LXx_VDOT   (const A, B       : V3    ) : T      ; inline;
    class function  LXx_VDOT3  (const A          : V3    ;
                                const X, Y, Z    : T     ) : T      ; inline;
    class function  LXx_VLEN   (const A          : V3    ) : T      ; inline;
    class function  LXx_VLENSQ (const A          : V3    ) : T      ; inline;
    class function  LXx_VDIST2 (const A, B       : V3    ) : T      ; inline;
    class function  LXx_VDIST  (const A, B       : V3    ) : T      ; inline;
    class procedure LXx_VCROSS (  out R          : V3    ;
                                const A, B       : V3    ) ;          inline;
    class procedure LXx_VLERP  (  out R          : V3    ;
                                const A, B       : V3    ;
                                const C          : T     ) ;          inline;
    class procedure LXx_V4LERP (  out R          : V4    ;
                                const A, B       : V4    ;
                                const C          : T     ) ;          inline;
    class function  LXx_LUMAFV (const A          : V3    ) : T      ; inline;

    class procedure LXx_COMPOSE_RGB(  out A      : V3    ;
                                    const B, C   : V3    ;
                                    const D      : T     ) ;          inline;

    class procedure LXx_HP_TO_VECTOR(  var V     : V3    ;
                                     const H, P  : T     ) ;          inline;

    class procedure LXx_VPERP  (  out A          : V3    ;
                                const B          : V3    ) ;          inline;

    class function  LXxCLAMP   (const A, B, C    : T     ) : T      ; inline;
    class function  LXxABS     (const A          : T     ) : T      ; inline;
    class function  LXxSIGN    (const A          : T     ) : T      ; inline;
  end;

  TVMath  = specialize TGVMath<Double>;
  TVMathF = specialize TGVMath<Single>;

  {$IFDEF SSE2_VMATH}
  // Optimized SSE2 math
  TVMathSSE2 = class(TVMathF)
    class procedure LXx_VSET   (  out A          : V3    ;
                                const X          : T     ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VCLR   (  out A          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class function  LXx_VEQS   (const A          : V3    ;
                                const X          : T     ):Boolean;assembler   ;
                                                                   nostackframe;
    class procedure LXx_VUNIT  (  out A          : V3    ;
                                const K          : UInt32) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VNEG   (  var V          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V1MIN  (  var A          : V3    ;
                                const X          : T     ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V1MAX  (  var A          : V3    ;
                                const X          : T     ) ;       assembler   ;
                                                                   nostackframe;
    class function  LXx_VNEZERO(const A          : V3    ):Boolean;assembler   ;
                                                                   nostackframe;
    class function  LXx_VEQ    (const A, B       : V3    ):Boolean;assembler   ;
                                                                   nostackframe;
    class procedure LXx_VCPY   (  out A          : V3    ;
                                const B          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VSCL   (  var A          : V3    ;
                                const X          : T     ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VADD   (  var A          : V3    ;
                                const B          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VSUB   (  var A          : V3    ;
                                const B          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VADDS  (  var A          : V3    ;
                                const B          : V3    ;
                                const X          : T     ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VMUL   (  var A          : V3    ;
                                const B          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VDIV   (  var A          : V3    ;
                                const B          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V3MIN  (  var A          : V3    ;
                                const B          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V3MAX  (  var A          : V3    ;
                                const B          : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VADD3  (  out R          : V3    ;
                                const A, B       : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VADDS3 (  out R          : V3    ;
                                const A, B       : V3    ;
                                const X          : T     ) ;       assembler   ;
    class procedure LXx_VSUB3  (  out R          : V3    ;
                                const A, B       : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VSCL3  (  out R          : V3    ;
                                const A          : V3    ;
                                const X          : T     ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VMUL3  (  out R          : V3    ;
                                const A, B       : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VMIN3  (  out R          : V3    ;
                                const A, B       : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VMAX3  (  out R          : V3    ;
                                const A, B       : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V4ZERO (  out A          : V4    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V4CPY  (  out A          : V4    ;
                                const B          : V4    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V4ADD  (  var A          : V4    ;
                                const B          : V4    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V4ADD3 (  out R          : V4    ;
                                const A, B       : V4    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V4ADDS (  var A          : V4    ;
                                const B          : V4    ;
                                const X          : T     ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V4ADDS3(  out R          : V4    ;
                                const A, B       : V4    ;
                                const X          : T     ) ;       assembler   ;
    class procedure LXx_V4SUB  (  var A          : V4    ;
                                const B          : V4    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V4SCL  (  var A          : V4    ;
                                const X          : T     ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_V4SCL3 (  out R          : V4    ;
                                const A          : V4    ;
                                const X          : T     )       ; assembler   ;
                                                                   nostackframe;
    class function  LXx_V4DOT  (const A, B       : V4    ): T    ; assembler   ;
                                                                   nostackframe;
    class function  LXx_VDOT   (const A, B       : V3    ) : T   ; assembler   ;
                                                                   nostackframe;
    class function  LXx_VDOT3  (const A          : V3    ;
                                const X, Y, Z    : T     ) : T   ; inline;
    class function  LXx_VLEN   (const A          : V3    ) : T   ; assembler   ;
                                                                   nostackframe;
    class function  LXx_VLENSQ (const A          : V3    ) : T   ; assembler   ;
                                                                   nostackframe;
    class function  LXx_VDIST2 (const A, B       : V3    ) : T   ; assembler   ;
                                                                   nostackframe;
    class function  LXx_VDIST  (const A, B       : V3    ) : T   ; assembler   ;
                                                                   nostackframe;
    class procedure LXx_VCROSS (  out R          : V3    ;
                                const A, B       : V3    ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VLERP  (  out R          : V3    ;
                                const A, B       : V3    ;
                                const C          : T     ) ;       assembler   ;
    class procedure LXx_V4LERP (  out R          : V4    ;
                                const A, B       : V4    ;
                                const C          : T     ) ;       assembler   ;
    class function  LXx_LUMAFV (const A          : V3    ) : T   ; assembler   ;
                                                                   nostackframe;
    class procedure LXx_COMPOSE_RGB(  out A      : V3    ;
                                    const B, C   : V3    ;
                                    const D      : T     ) ;       assembler   ;
    class procedure LXx_HP_TO_VECTOR(  var V     : V3    ;
                                     const H, P  : T     ) ;       assembler   ;
                                                                   nostackframe;
    class procedure LXx_VPERP  (  out A          : V3    ;
                                const B          : V3    ) ;       assembler   ;
                                                                   nostackframe;
  end;
  {$ENDIF}

function SCHLICK_BIAS     (const X, B : Single) : Single;
function SCHLICK_GAIN     (const X, G : Single) : Single;
function LXx_BYTE_TO_FLOAT(const B    : Byte  ) : Single;
function LXx_FLOAT_TO_BYTE(const F    : Single) : Byte  ;

function Vec(X, Y, Z : Double) : LXtVector ; overload;
function Vec(X, Y, Z : Single) : LXtFVector; overload;

function Dot(const V1, V2 : LXtVector) : Double;
function Len(const V : LXtVector) : Double;

operator := (const VR : LXtVector ) VL : LXtFVector;
operator := (const X  : Double    ) VR : LXtVector ;

operator := (const VR : LXtFVector) VL : LXtVector ;

operator +  (const V1, V2 : LXtFVector) VR : LXtFVector;
operator -  (const V1, V2 : LXtVector) VR : LXtVector;
operator -  (const V : LXtVector) VR : LXtVector;

operator *  (const V : LXtVector;
             const X : Double   ) VR : LXtVector;

implementation

type
  S = Single;

function SCHLICK_BIAS(const X, B: Single) : Single; inline; nostackframe;
begin
  Result := X / ((S(1.0) / B - S(2.0)) * (S(1.0) - X) + S(1.0));
end;
function SCHLICK_GAIN(const X, G: Single) : Single; inline; nostackframe;
begin
  if X < S(0.5) then
    Result := X / ((S(1.0) / G - S(2.0)) * (S(1.0) - S(2.0) * X) + S(1.0))
  else
    Result := ((S(1.0) / G - S(2.0)) * (S(1.0) - S(2.0) * X) - X) /
              ((S(1.0) / G - S(2.0)) * (S(1.0) - S(2.0) * X) - S(1.0));
end;

function LXx_BYTE_TO_FLOAT(const B: Byte): Single; inline; nostackframe;
begin
  Result := S(B) * LXx_BYTE_FRACTION;
end;

function LXx_FLOAT_TO_BYTE(const F: Single): Byte; inline; nostackframe;
begin
  Result := Round(F * S(255.0));
end;

function Vec(X, Y, Z : Double) : LXtVector; inline;
begin
  Result[0] := X;
  Result[1] := Y;
  Result[2] := Z;
end;

function Vec(X, Y, Z : Single) : LXtFVector; inline;
begin
  Result[0] := X;
  Result[1] := Y;
  Result[2] := Z;
end;

function Dot(const V1, V2 : LXtVector) : Double; inline;
begin
  Result := (V1[0] * V2[0]) +
            (V1[1] * V2[1]) +
            (V1[2] * V2[2])  ;
end;

function Len(const V : LXtVector) : Double; inline;
begin
  Result := Sqrt(Dot(V, V));
end;

operator := (const VR : LXtVector) VL : LXtFVector; inline;
begin
  VL[0] := VR[0];
  VL[1] := VR[1];
  VL[2] := VR[2];
end;

operator := (const X : Double) VR : LXtVector; inline;
begin
  VR[0] := X;
  VR[1] := X;
  VR[2] := X;
end;

operator := (const VR: LXtFVector) VL : LXtVector; inline;
begin
  VL[0] := VR[0];
  VL[1] := VR[1];
  VL[2] := VR[2];
end;

operator + (const V1, V2 : LXtFVector) VR : LXtFVector; inline;
begin
  VR[0] := V1[0] + V2[0];
  VR[1] := V1[1] + V2[1];
  VR[2] := V1[2] + V2[2];
end;

operator - (const V1, V2 : LXtVector) VR : LXtVector; inline;
begin
  VR[0] := V1[0] - V2[0];
  VR[1] := V1[1] - V2[1];
  VR[2] := V1[2] - V2[2];
end;

operator - (const V : LXtVector)VR : LXtVector; inline;
begin
  VR[0] := -V[0];
  VR[1] := -V[1];
  VR[2] := -V[2];
end;

operator * (const V : LXtVector; const X : Double) VR : LXtVector; inline;
begin
  VR[0] := V[0] * X;
  VR[1] := V[1] * X;
  VR[2] := V[2] * X;
end;

{$IFDEF SSE2_VMATH}
class procedure TVMathSSE2.LXx_VSET(out A: V3; const X: T);
asm
  SHUFPS   X   , X, $C0
  MOVAPS   A[0], X
end;

class procedure TVMathSSE2.LXx_VCLR(out A: V3);
asm
  XORPS    XMM0, XMM0
  MOVAPS   A[0], XMM0
end;

class function TVMathSSE2.LXx_VEQS(const A: V3; const X: T): Boolean;
asm
  MOVAPS   XMM0, A[0]
  SHUFPS   X   , X   , $C0
  CMPEQPS  XMM0, X
  PMOVMSKB EAX , XMM0
  CMP      EAX , $FFFF
  JE       @@Exit
  XOR      EAX , EAX
@@Exit:
end;

class procedure TVMathSSE2.LXx_VUNIT(out A: V3; const K: UInt32);
asm
  XORPS    XMM0, XMM0
  MOVAPS   A[0], XMM0
  MOV      DWORD PTR [A + R8 * 4], $3F800000  // $3F800000 = Single(1.0)
end;

class procedure TVMathSSE2.LXx_VNEG(var V: V3);
asm
  MOV      EAX , $80000000  // Sign bit
  MOVD     XMM1, EAX
  SHUFPS   XMM1, XMM1, $C0
  XORPS    XMM1, V[0]
  MOVAPS   V[0], XMM1
end;

class procedure TVMathSSE2.LXx_V1MIN(var A: V3; const X: T);
asm
  MOVAPS   XMM0, A[0]
  SHUFPS   X   , X   , $C0
  MINPS    XMM0, X
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V1MAX(var A: V3; const X: T);
asm
  MOVAPS   XMM0, A[0]
  SHUFPS   X   , X   , $C0
  MAXPS    XMM0, X
  MOVAPS   A[0], XMM0
end;

class function TVMathSSE2.LXx_VNEZERO(const A: V3): Boolean;
asm
  XORPS    XMM0, XMM0
  CMPNEQPS XMM0, A[0]
  PMOVMSKB EAX , XMM0
end;

class function TVMathSSE2.LXx_VEQ(const A, B: V3): Boolean;
asm
  MOVAPS   XMM0, A[0]
  CMPEQPS  XMM0, B[0]
  PMOVMSKB EAX , XMM0
  CMP      EAX , $FFFF
  JE       @@Exit
  XOR      EAX , EAX
@@Exit:
end;

class procedure TVMathSSE2.LXx_VCPY(out A: V3; const B: V3);
asm
  MOVAPS   XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_VSCL(var A: V3; const X: T);
asm
  MOVAPS   XMM0, A[0]
  SHUFPS   X   , X   , $C0
  MULPS    XMM0, X
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_VADD(var A: V3; const B: V3);
asm
  MOVAPS   XMM0, A[0]
  ADDPS    XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_VSUB(var A: V3; const B: V3);
asm
  MOVAPS   XMM0, A[0]
  SUBPS    XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_VADDS(var A: V3; const B: V3; const X: T);
asm
  MOVAPS   XMM0, A[0]
  MOVAPS   XMM1, B[0]
  SHUFPS   X   , X   , $C0
  MULPS    XMM1, X
  ADDPS    XMM0, XMM1
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_VMUL(var A: V3; const B: V3);
asm
  MOVAPS   XMM0, A[0]
  MULPS    XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_VDIV(var A: V3; const B: V3);
asm
  MOVAPS   XMM0, A[0]
  MOVAPS   XMM1, B[0]
  SHUFPS   XMM1, XMM1, $24  // Copy B[0] to B[3] preventing a div by zero case
  DIVPS    XMM0, XMM1
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V3MIN(var A: V3; const B: V3);
asm
  MOVAPS   XMM0, A[0]
  MINPS    XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V3MAX(var A: V3; const B: V3);
asm
  MOVAPS   XMM0, A[0]
  MAXPS    XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_VADD3(out R: V3; const A, B: V3);
asm
  MOVAPS   XMM0, A[0]
  ADDPS    XMM0, B[0]
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_VADDS3(out R: V3; const A, B: V3; const X: T);
asm
  MOVAPS   XMM0, A[0]
  MOVAPS   XMM1, B[0]
  MOVSS    XMM2, X
  SHUFPS   XMM2, XMM2, $C0
  MULPS    XMM1, XMM2
  ADDPS    XMM0, XMM1
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_VSUB3(out R: V3; const A, B: V3);
asm
  MOVAPS   XMM0, A[0]
  SUBPS    XMM0, B[0]
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_VSCL3(out R: V3; const A: V3; const X: T);
asm
  MOVAPS   XMM0, A[0]
  SHUFPS   X   , X   , $C0
  MULPS    XMM0, X
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_VMUL3(out R: V3; const A, B: V3);
asm
  MOVAPS   XMM0, A[0]
  MULPS    XMM0, B[0]
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_VMIN3(out R: V3; const A, B: V3);
asm
  MOVAPS   XMM0, A[0]
  MINPS    XMM0, B[0]
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_VMAX3(out R: V3; const A, B: V3);
asm
  MOVAPS   XMM0, A[0]
  MAXPS    XMM0, B[0]
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4ZERO(out A: V4);
asm
  XORPS    XMM0, XMM0
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4CPY(out A: V4; const B: V4);
asm
  MOVAPS   XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4ADD(var A: V4; const B: V4);
asm
  MOVAPS   XMM0, A[0]
  ADDPS    XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4ADD3(out R: V4; const A, B: V4);
asm
  MOVAPS   XMM0, A[0]
  ADDPS    XMM0, B[0]
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4ADDS(var A: V4; const B: V4; const X: T);
asm
  MOVAPS   XMM0, A[0]
  MOVAPS   XMM1, B[0]
  SHUFPS   X   , X   , $0
  MULPS    XMM1, X
  ADDPS    XMM0, XMM1
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4ADDS3(out R: V4; const A, B: V4; const X: T);
asm
  MOVAPS   XMM0, A[0]
  MOVAPS   XMM1, B[0]
  MOVSS    XMM2, X
  SHUFPS   XMM2, XMM2, $0
  MULPS    XMM1, XMM2
  ADDPS    XMM0, XMM1
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4SUB(var A: V4; const B: V4);
asm
  MOVAPS   XMM0, A[0]
  SUBPS    XMM0, B[0]
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4SCL(var A: V4; const X: T);
asm
  MOVAPS   XMM0, A[0]
  SHUFPS   X   , X   , $0
  MULPS    XMM0, X
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4SCL3(out R: V4; const A: V4; const X: T);
asm
  MOVAPS   XMM0, A[0]
  SHUFPS   X   , X   , $0
  MULPS    XMM0, X
  MOVAPS   R[0], XMM0
end;

class function TVMathSSE2.LXx_V4DOT(const A, B: V4): T;
asm
  MOVAPS   XMM0, A[0]
  MULPS    XMM0, B[0]
  PSHUFD   XMM1, XMM0, $31  // 1 -> 0, 3 -> 2
  ADDPS    XMM0, XMM1       // 0 +  1 , XX, 2 + 3, XX
  PSHUFD   XMM1, XMM0, $2   // 2 -> 0
  ADDPS    XMM0, XMM1
end;

class function TVMathSSE2.LXx_VDOT(const A, B: V3): T;
asm
  MOVAPS   XMM0, A[0]
  MULPS    XMM0, B[0]
  PSHUFD   XMM1, XMM0, $31
  ADDPS    XMM0, XMM1
  PSHUFD   XMM1, XMM0, $2
  ADDPS    XMM0, XMM1
end;

class function TVMathSSE2.LXx_VDOT3(const A: V3; const X, Y, Z: T): T;
var
  B : V3;
begin
  LXx_VSET3(B, X, Y, Z);
  Result := LXx_VDOT (A, B);
end;

class function TVMathSSE2.LXx_VLEN(const A: V3): T;
asm
  MOVAPS   XMM0, A[0]
  MULPS    XMM0, XMM0
  PSHUFD   XMM1, XMM0, $31
  ADDPS    XMM0, XMM1
  PSHUFD   XMM1, XMM0, $2
  ADDPS    XMM0, XMM1
  SQRTSS   XMM0, XMM0
end;

class function TVMathSSE2.LXx_VLENSQ(const A: V3): T;
asm
  MOVAPS   XMM0, A[0]
  MULPS    XMM0, XMM0
  PSHUFD   XMM1, XMM0, $31
  ADDPS    XMM0, XMM1
  PSHUFD   XMM1, XMM0, $2
  ADDPS    XMM0, XMM1
end;

class function TVMathSSE2.LXx_VDIST2(const A, B: V3): T;
asm
  MOVAPS   XMM0, A[0]
  SUBPS    XMM0, B[0]
  MULPS    XMM0, XMM0
  PSHUFD   XMM1, XMM0, $31
  ADDPS    XMM0, XMM1
  PSHUFD   XMM1, XMM0, $2
  ADDPS    XMM0, XMM1
end;

class function TVMathSSE2.LXx_VDIST(const A, B: V3): T;
asm
  MOVAPS   XMM0, A[0]
  SUBPS    XMM0, B[0]
  MULPS    XMM0, XMM0
  PSHUFD   XMM1, XMM0, $31
  ADDPS    XMM0, XMM1
  PSHUFD   XMM1, XMM0, $2
  ADDPS    XMM0, XMM1
  SQRTSS   XMM0, XMM0
end;

class procedure TVMathSSE2.LXx_VCROSS(out R: V3; const A, B: V3);
asm
  MOVAPS   XMM0, A[0]       // Move aligned vectors to SSE regs
  MOVAPS   XMM1, B[0]
  MOVAPS   XMM2, XMM0       // Make a copy of vector A
  MOVAPS   XMM3, XMM1       // Make a copy of vector B

  SHUFPS   XMM0, XMM0, $D8  // 11 01 10 00 Flip the middle elements of A
  SHUFPS   XMM1, XMM1, $E1  // 11 10 00 01 Flip first two elements of B
  MULPS    XMM0, XMM1       // Multiply the modified register vectors

  SHUFPS   XMM2, XMM2, $E1  // 11 10 00 01 Flip first two elements of the A copy
  SHUFPS   XMM3, XMM3, $D8  // 11 01 10 00 Flip the middle elements of the B copy
  MULPS    XMM2, XMM3       // Multiply the modified register vectors

  SUBPS    XMM0, XMM2       // Subtract the two resulting register vectors

  MOVAPS   R[0], XMM0       // Save the return vector
end;

class procedure TVMathSSE2.LXx_VLERP(out R: V3; const A, B: V3; const C: T);
asm
  MOVAPS   XMM0, A[0]
  MOVAPS   XMM1, B[0]
  SUBPS    XMM1, XMM0
  MOVSS    XMM2, C
  SHUFPS   XMM2, XMM2, $C0
  MULPS    XMM1, XMM2
  ADDPS    XMM0, XMM1
  MOVAPS   R[0], XMM0
end;

class procedure TVMathSSE2.LXx_V4LERP(out R: V4; const A, B: V4; const C: T);
asm
  MOVAPS   XMM0, A[0]
  MOVAPS   XMM1, B[0]
  SUBPS    XMM1, XMM0
  MOVSS    XMM2, C
  SHUFPS   XMM2, XMM2, $0
  MULPS    XMM1, XMM2
  ADDPS    XMM0, XMM1
  MOVAPS   R[0], XMM0
end;

class function TVMathSSE2.LXx_LUMAFV(const A: V3): T;
const
  C : array [0..2] of Single = (0.30, 0.59, 0.11);
asm
  MOVAPS   XMM0, A[0]
  MULPS    XMM0, [RIP + C]
  PSHUFD   XMM1, XMM0, $31
  ADDPS    XMM0, XMM1
  PSHUFD   XMM1, XMM0, $2
  ADDPS    XMM0, XMM1
end;

class procedure TVMathSSE2.LXx_COMPOSE_RGB(out A: V3; const B, C: V3; const D: T);
asm
  MOVSS    XMM2, D
  SHUFPS   XMM2, XMM2, $C0
  MOVAPS   XMM1, B[0]
  MOVAPS   XMM0, XMM1
  SUBPS    XMM1, C[0]
  MULPS    XMM2, XMM1
  SUBPS    XMM0, XMM2
  MOVAPS   A[0], XMM0
end;

class procedure TVMathSSE2.LXx_HP_TO_VECTOR(var V: V3; const H, P: T);
const
  Temp : array [0..3] of Single = (0, 0, 0, 0);
asm
  MOVD     [RIP + Temp    ], P
  MOVD     [RIP + Temp + 4], H

  FLD      DWORD PTR [RIP + Temp     ]
  FSINCOS
  FLD      DWORD PTR [RIP + Temp + 4 ]
  FSINCOS

  FSTP     DWORD PTR [RIP + Temp     ]
  FSTP     DWORD PTR [RIP + Temp + 8 ]
  FSTP     DWORD PTR [RIP + Temp + 12]
  FSTP     DWORD PTR [RIP + Temp + 4 ]

  MOVAPS   XMM0, [RIP + Temp]
  MOV      EAX , $3F800000
  MOVD     XMM1, EAX
  SHUFPS   XMM1, XMM0, $FC
  PSHUFD   XMM1, XMM1, $73
  MULPS    XMM0, XMM1
  MOVAPS   V[0], XMM0
end;

class procedure TVMathSSE2.LXx_VPERP(out A: V3; const B: V3);
asm
  MOVAPS   XMM2, B[0]
  PSHUFD   XMM0, XMM2, $C9
  PSHUFD   XMM1, XMM0, $C9
  SUBPS    XMM0, XMM1
  MOVAPS   A[0], XMM0
end;

{$ENDIF}

class function TGVMath.IfThen(const C: Boolean; RT, RF: T): T;
begin
  if C then
    Result := RT
  else
    Result := RF;
end;

class procedure TGVMath.LXx_VSET(out A: V3; const X: T);
begin
  A[0] := X;
  A[1] := X;
  A[2] := X;
end;

class procedure TGVMath.LXx_VCLR(out A: V3);
begin
  LXx_VSET(A, T(0));
end;

class procedure TGVMath.LXx_VSET3(out A: V3; const X, Y, Z: T);
begin
  A[0] := X;
  A[1] := Y;
  A[2] := Z;
end;

class function TGVMath.LXx_VEQS(const A: V3; const X: T): Boolean;
begin
  Result := (A[0] = X) and
            (A[1] = X) and
            (A[2] = X)    ;
end;

class procedure TGVMath.LXx_VUNIT(out A: V3; const K: UInt32);
begin
  LXx_VCLR(A);
  A[K] := T(1);
end;

class procedure TGVMath.LXx_VNEG(var V: V3);
begin
  V[0] := -V[0];
  V[1] := -V[1];
  V[2] := -V[2];
end;

class procedure TGVMath.LXx_VUOP(var V: V3; const Op: TOp1);
begin
  V[0] := Op(V[0]);
  V[1] := Op(V[1]);
  V[2] := Op(V[2]);
end;

class procedure TGVMath.LXx_VUOP2(var X, V: V3; const Op: TOp1);
begin
  X[0] := Op(V[0]);
  X[1] := Op(V[1]);
  X[2] := Op(V[2]);
end;

class procedure TGVMath.LXx_V1MIN(var A: V3; const X: T);
begin
  A[0] := IfThen(A[0] > X, X, A[0]);
  A[1] := IfThen(A[1] > X, X, A[1]);
  A[2] := IfThen(A[2] > X, X, A[2]);
end;

class procedure TGVMath.LXx_V1MAX(var A: V3; const X: T);
begin
  A[0] := IfThen(A[0] < X, X, A[0]);
  A[1] := IfThen(A[1] < X, X, A[1]);
  A[2] := IfThen(A[2] < X, X, A[2]);
end;

class function TGVMath.LXx_VNEZERO(const A: V3): Boolean;
begin
  Result := (A[0] <> T(0)) or
            (A[1] <> T(0)) or
            (A[2] <> T(0))   ;
end;

class procedure TGVMath.LXx_V3SET(out A: V3; const X, Y, Z: T);
begin
  LXx_VSET3(A, X, Y, Z);
end;

class function TGVMath.LXx_VEQ(const A, B: V3): Boolean;
begin
  Result := (A[0] = B[0]) and
            (A[1] = B[1]) and
            (A[2] = B[2])    ;
end;

class procedure TGVMath.LXx_VCPY(out A: V3; const B: V3);
begin
  A[0] := B[0];
  A[1] := B[1];
  A[2] := B[2];
end;

class procedure TGVMath.LXx_VSCL(var A: V3; const X: T);
begin
  A[0] := A[0] * X;
  A[1] := A[1] * X;
  A[2] := A[2] * X;
end;

class procedure TGVMath.LXx_VADD(var A: V3; const B: V3);
begin
  A[0] := A[0] + B[0];
  A[1] := A[1] + B[1];
  A[2] := A[2] + B[2];
end;

class procedure TGVMath.LXx_VSUB(var A: V3; const B: V3);
begin
  A[0] := A[0] - B[0];
  A[1] := A[1] - B[1];
  A[2] := A[2] - B[2];
end;

class procedure TGVMath.LXx_VADDS(var A: V3; const B: V3; const X: T);
begin
  A[0] := A[0] + (B[0] * X);
  A[1] := A[1] + (B[1] * X);
  A[2] := A[2] + (B[2] * X);
end;

class procedure TGVMath.LXx_VMUL(var A: V3; const B: V3);
begin
  A[0] := A[0] * B[0];
  A[1] := A[1] * B[1];
  A[2] := A[2] * B[2];
end;

class procedure TGVMath.LXx_VDIV(var A: V3; const B: V3);
begin
  A[0] := A[0] / B[0];
  A[1] := A[1] / B[1];
  A[2] := A[2] / B[2];
end;

class procedure TGVMath.LXx_VBOP(var A: V3; const B: V3; const Op: TOp2);
begin
  A[0] := Op(A[0], B[0]);
  A[1] := Op(A[1], B[1]);
  A[2] := Op(A[2], B[2]);
end;

class function TGVMath.LXxMAX(const A, B: T): T;
begin
  Result := IfThen(A > B, A, B);
end;

class function TGVMath.LXxMIN(const A, B: T): T;
begin
  Result := IfThen(A <= B, A, B);
end;

class procedure TGVMath.LXx_V3MIN(var A: V3; const B: V3);
begin
  LXx_VBOP(A, B, @LXxMIN);
end;

class procedure TGVMath.LXx_V3MAX(var A: V3; const B: V3);
begin
  LXx_VBOP(A, B, @LXxMAX);
end;

class procedure TGVMath.LXx_VADD3(out R: V3; const A, B: V3);
begin
  R[0] := A[0] + B[0];
  R[1] := A[1] + B[1];
  R[2] := A[2] + B[2];
end;

class procedure TGVMath.LXx_VADDS3(out R: V3; const A, B: V3; const X: T);
begin
  R[0] := A[0] + (B[0] * X);
  R[1] := A[1] + (B[1] * X);
  R[2] := A[2] + (B[2] * X);
end;

class procedure TGVMath.LXx_VSUB3(out R: V3; const A, B: V3);
begin
  R[0] := A[0] - B[0];
  R[1] := A[1] - B[1];
  R[2] := A[2] - B[2];
end;

class procedure TGVMath.LXx_VSCL3(out R: V3; const A: V3; const X: T);
begin
  R[0] := A[0] * X;
  R[1] := A[1] * X;
  R[2] := A[2] * X;
end;

class procedure TGVMath.LXx_VMUL3(out R: V3; const A, B: V3);
begin
  R[0] := A[0] * B[0];
  R[1] := A[1] * B[1];
  R[2] := A[2] * B[2];
end;

class procedure TGVMath.LXx_VBOP3(out R: V3; const A, B: V3; const Op: TOp2);
begin
  R[0] := Op(A[0], B[0]);
  R[1] := Op(A[1], B[1]);
  R[2] := Op(A[2], B[2]);
end;

class procedure TGVMath.LXx_VMIN3(out R: V3; const A, B: V3);
begin
  LXx_VBOP3(R, A, B, @LXxMIN);
end;

class procedure TGVMath.LXx_VMAX3(out R: V3; const A, B: V3);
begin
  LXx_VBOP3(R, A, B, @LXxMAX);
end;

class procedure TGVMath.LXx_V4SET4(out A: V4; const X, Y, Z, W: T);
begin
  A[0] := X;
  A[1] := Y;
  A[2] := Z;
  A[3] := W;
end;

class procedure TGVMath.LXx_V4BLACK(out A: V4);
begin
  LXx_V4SET4(A, T(0), T(0), T(0), T(1));
end;

class procedure TGVMath.LXx_V4ZERO(out A: V4);
begin
  LXx_V4SET4(A, T(0), T(0), T(0), T(0));
end;

class procedure TGVMath.LXx_V4SET(out A: V4; const X: T);
begin
  LXx_V4SET4(A, X, X, X, X);
end;

class procedure TGVMath.LXx_V4CPY(out A: V4; const B: V4);
begin
  A[0] := B[0];
  A[1] := B[1];
  A[2] := B[2];
  A[3] := B[3];
end;

class procedure TGVMath.LXx_V4ADD(var A: V4; const B: V4);
begin
  A[0] := A[0] + B[0];
  A[1] := A[1] + B[1];
  A[2] := A[2] + B[2];
  A[3] := A[3] + B[3];
end;

class procedure TGVMath.LXx_V4ADD3(out R: V4; const A, B: V4);
begin
  R[0] := A[0] + B[0];
  R[1] := A[1] + B[1];
  R[2] := A[2] + B[2];
  R[3] := A[3] + B[3];
end;

class procedure TGVMath.LXx_V4ADDS(var A: V4; const B: V4; const X: T);
begin
  A[0] := A[0] + (B[0] * X);
  A[1] := A[1] + (B[1] * X);
  A[2] := A[2] + (B[2] * X);
  A[3] := A[3] + (B[3] * X);
end;

class procedure TGVMath.LXx_V4ADDS3(out R: V4; const A, B: V4; const X: T);
begin
  R[0] := A[0] + (B[0] * X);
  R[1] := A[1] + (B[1] * X);
  R[2] := A[2] + (B[2] * X);
  R[3] := A[3] + (B[3] * X);
end;

class procedure TGVMath.LXx_V4SUB(var A: V4; const B: V4);
begin
  A[0] := A[0] - B[0];
  A[1] := A[1] - B[1];
  A[2] := A[2] - B[2];
  A[3] := A[3] - B[3];
end;

class procedure TGVMath.LXx_V4SCL(var A: V4; const X: T);
begin
  A[0] := A[0] * X;
  A[1] := A[1] * X;
  A[2] := A[2] * X;
  A[3] := A[3] * X;
end;

class procedure TGVMath.LXx_V4SCL3(out R: V4; const A: V4; const X: T);
begin
  R[0] := A[0] * X;
  R[1] := A[1] * X;
  R[2] := A[2] * X;
  R[3] := A[3] * X;
end;

class function TGVMath.LXx_V4DOT(const A, B: V4): T;
begin
  Result := (A[0] * B[0]) +
            (A[1] * B[1]) +
            (A[2] * B[2]) +
            (A[3] * B[3])  ;
end;

class function TGVMath.LXx_VDOT(const A, B: V3): T;
begin
  Result := (A[0] * B[0]) +
            (A[1] * B[1]) +
            (A[2] * B[2])  ;
end;

class function TGVMath.LXx_VDOT3(const A: V3; const X, Y, Z: T): T;
begin
  Result := (A[0] * X) +
            (A[1] * Y) +
            (A[2] * Z)  ;
end;

class function TGVMath.LXx_VLEN(const A: V3): T;
begin
  Result := Sqrt(LXx_VDOT(A, A));
end;

class function TGVMath.LXx_VLENSQ(const A: V3): T;
begin
  Result := LXx_VDOT(A, A);
end;

class function TGVMath.LXx_VDIST2(const A, B: V3): T;
begin
  Result := ((A[0] - B[0]) * (A[0] - B[0])) +
            ((A[1] - B[1]) * (A[1] - B[1])) +
            ((A[2] - B[2]) * (A[2] - B[2]))  ;
end;

class function TGVMath.LXx_VDIST(const A, B: V3): T;
begin
  Result := Sqrt(LXx_VDIST2(A, B));
end;

class procedure TGVMath.LXx_VCROSS(out R: V3; const A, B: V3);
begin
  R[0] := (A[1] * B[2]) - (A[2] * B[1]);
  R[1] := (A[2] * B[0]) - (A[0] * B[2]);
  R[2] := (A[0] * B[1]) - (A[1] * B[0]);
end;

class procedure TGVMath.LXx_VLERP(out R: V3; const A, B: V3; const C: T);
begin
  R[0] := A[0] + (C * (B[0] - A[0]));
  R[1] := A[1] + (C * (B[1] - A[1]));
  R[2] := A[2] + (C * (B[2] - A[2]));
end;

class procedure TGVMath.LXx_V4LERP(out R: V4; const A, B: V4; const C: T);
begin
  R[0] := A[0] + (C * (B[0] - A[0]));
  R[1] := A[1] + (C * (B[1] - A[1]));
  R[2] := A[2] + (C * (B[2] - A[2]));
  R[3] := A[3] + (C * (B[3] - A[3]));
end;

class function TGVMath.LXx_LUMAFV(const A: V3): T;
begin
  Result := (Single(0.30) * A[0]) +
            (Single(0.59) * A[1]) +
            (Single(0.11) * A[2])  ;
end;

class procedure TGVMath.LXx_COMPOSE_RGB(out A: V3; const B, C: V3; const D: T);
begin
  A[0] := (B[0] * (1 - D)) + (C[0] * D);
  A[1] := (B[1] * (1 - D)) + (C[1] * D);
  A[2] := (B[2] * (1 - D)) + (C[2] * D);
end;

class procedure TGVMath.LXx_HP_TO_VECTOR(var V: V3; const H, P: T);
begin
  V[2] := Cos(P);
  V[0] := Cos(H) * V[2];
  V[1] := Sin(P);
  V[2] := V[2] * Sin(H);
end;

class procedure TGVMath.LXx_VPERP(out A: V3; const B: V3);
begin
  A[0] := B[1] - B[2];
  A[1] := B[2] - B[0];
  A[2] := B[0] - B[1];
end;

class function TGVMath.LXxCLAMP(const A, B, C: T): T;
begin
  Result := IfThen(A < B, B, IfThen(A > C, C, A));
end;

class function TGVMath.LXxABS(const A: T): T;
begin
  Result := IfThen(A < 0, -A, A);
end;

class function TGVMath.LXxSIGN(const A: T): T;
begin
  Result := IfThen(A < 0, -1, 1);
end;

end.

